import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

public class Listener implements Runnable
{
   private ServerSocket serverSocket;
   private int name;
   
   public Listener(int name, int port) throws IOException
   {
	  this.name = name;
	  serverSocket = new ServerSocket(port);
	  serverSocket.setSoTimeout(1000000);
	  new Thread(this, name+"").start();
   }
   
   public void run()
   {
      while(true)
      {
         try
         {
            Socket server = serverSocket.accept();
            ObjectInputStream in =
                  new ObjectInputStream(server.getInputStream());
            Object obj = in.readObject();
            if (obj instanceof MarkerMessage) {
            	MarkerMessage markerMsg = (MarkerMessage) obj;
            	try {
					Thread.sleep((new Random(markerMsg.getSenderProcess()).nextInt(4000)+4000));
				} catch (InterruptedException e) {
					System.out.println("InterruptedException caught in Sender");
					System.exit(1);
				}
            	System.out.println("Marker Message received from Process " + markerMsg.getSenderProcess() + " at " + new Date(System.currentTimeMillis()));
            	if (!Process.snapshotInProgress && !Process.initiatedSnapshot) {
            		Process.snapshotInProgress = true;
            		//id3:snapshot1:logical2:vector0011:money5 widgets3
            		String str = "id" + Process.name + " : " + "snapshot" + markerMsg.getSnapshotName() + " : " 
                    		+ "logical " + Process.lamportTimestamp + " : "
                    		+ "vector " + Process.printVector(Process.vectorTimestamp) + " : "
                    		+ "money " + Process.money
                    		+ " widget " + Process.widget
                    		+ "\n";
            		Process.writeToFile(str);
            		
            		for (int i = 0; i < ProcessManager.numProcess; i++) {
                		if (i != markerMsg.getSenderProcess() && i != Process.name) {
                			Process.receivedMessages.put(new Integer(i), new ArrayList<NormalMessage>());
                		}
                	}
            		markerMsg.setSenderProcess(Process.name);
            		markerMsg.setReceipientsToAllOthers();
            		new Sender(markerMsg);
            	} else {
            		for (NormalMessage msg : Process.receivedMessages.get(markerMsg.getSenderProcess())) {
            			//id3:snapshot1: logical1:vector0100:message2to3:money10widgets100
                		String str = "id" + Process.name + " : " + "snapshot" + markerMsg.getSnapshotName() + " : " 
                		+ "logical " + msg.getLamportTimestamp() + " : "
                		+ "vector " + Process.printVector(msg.getVectorTimestamp()) + " : "
                		+ "message " + markerMsg.getSenderProcess() + " to " + Process.name 
                		+ " : money " + msg.getMoney() 
                		+ " widget " + msg.getWidget()
                		+ "\n";
                		Process.writeToFile(str);
            		}
            		Process.receivedMessages.remove(markerMsg.getSenderProcess());
            		if (Process.receivedMessages.isEmpty()) {
            			Process.snapshotInProgress = false;
            			Process.initiatedSnapshot = false;
            		}
            	}
            } else if (obj instanceof NormalMessage){
            	NormalMessage inMsg = (NormalMessage) obj;
            	if (Process.receivedMessages.containsKey(inMsg.getSenderProcess())) {
            		Process.receivedMessages.get(inMsg.getSenderProcess()).add(inMsg);
            	}
//            	System.out.println("Old Timestamp: " + Process.lamportTimestamp);
	            // lamport timestamp
	            if (inMsg.getLamportTimestamp() > Process.lamportTimestamp) {
	            	Process.lamportTimestamp = inMsg.getLamportTimestamp();
	            }
	            Process.lamportTimestamp++;

//	            System.out.println("Received message from Process " + inMsg.getSenderProcess());
//	            System.out.println("Receive Timestamp: " + inMsg.getLamportTimestamp());
//	            System.out.println("New Timestamp: " + Process.lamportTimestamp);
	            
	            // vector timestamp
	            int[] newVecTimestamp = Process.vectorTimestamp;
	            for (int i = 0; i < ProcessManager.numProcess; i++) {
		            if (inMsg.getVectorTimestamp()[i] > newVecTimestamp[i]) {
		            	newVecTimestamp[i] = inMsg.getVectorTimestamp()[i];
		            }
	            }
	            newVecTimestamp[name]++;
//	            System.out.println("Receive Vector Timestamp: " + newVecTimestamp[0] + " " +
//	            		newVecTimestamp[1] + " " + newVecTimestamp[2] + " " + newVecTimestamp[3]);
//	            System.out.println();
	            Process.vectorTimestamp = newVecTimestamp;
	            
	            // Money and Widgets
	            Process.money += inMsg.getMoney();
	            Process.widget += inMsg.getWidget();
	            
//	            System.out.println("Process " + name + " receiving " + inMsg.getMoney() + " money " + inMsg.getWidget() + " widget");
//				System.out.println("Process " + name + " is left with " + Process.money + " money " + Process.widget + " widget");
	        } else if (obj instanceof SnapshotTriggeringMessage) {
	        	Process.snapshot(((SnapshotTriggeringMessage) obj).getName());
	        }
            server.close();
         }catch(SocketTimeoutException s)
         {
            System.out.println("Socket timed out!");
            break;
         }catch(IOException e)
         {
            e.printStackTrace();
            break;
         } catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
      }
   }
}